package com.openweixin.sheknows.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.openweixin.sheknows.common.Pager;
import com.openweixin.sheknows.common.Pager.Param;
import com.openweixin.sheknows.dao.BaseDao;

/**
 * 描述：Dao实现类 - Dao实现类基类
 * 注意事项：在该类中对参数不做校验
 * @author 刘思作
 * @version 1.0.0 2014-04-01
 * @since v1.0.0
 */

@Repository
public class BaseDaoImpl<T, PK extends Serializable> implements BaseDao<T, PK> {
	
	private Class<T> entityClass;
	protected SessionFactory sessionFactory;

	
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		this.entityClass = null;
        Class<?> c = getClass();
        Type type = c.getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            Type[] parameterizedType = ((ParameterizedType) type).getActualTypeArguments();
            this.entityClass = (Class<T>) parameterizedType[0];
        }
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	protected Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	@SuppressWarnings("unchecked")
	public T get(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().get(entityClass, id);
	}

	@SuppressWarnings("unchecked")
	public T load(PK id) {
		Assert.notNull(id, "id is required");
		return (T) getSession().load(entityClass, id);
	}
	
	@SuppressWarnings("unchecked")
	public List<T> get(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		String hql = "from " + entityClass.getName() + " as model where model.id in(:ids)";
		return getSession().createQuery(hql).setParameterList("ids", ids).list();
	}

	@SuppressWarnings("unchecked")
	public T get(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return (T) getSession().createQuery(hql).setParameter(0, value).uniqueResult();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getList(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		String hql = "from " + entityClass.getName() + " as model where model." + propertyName + " = ?";
		return getSession().createQuery(hql).setParameter(0, value).list();
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		String hql = "from " + entityClass.getName();
		return getSession().createQuery(hql).list();
	}
	
	public Long getTotalCount() {
		String hql = "select count(*) from " + entityClass.getName();
		return (Long) getSession().createQuery(hql).uniqueResult();
	}

	public boolean isUnique(String propertyName, Object oldValue, Object newValue) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(newValue, "newValue is required");
		if (newValue == oldValue || newValue.equals(oldValue)) {
			return true;
		}
		if (newValue instanceof String) {
			if (oldValue != null && StringUtils.equalsIgnoreCase((String) oldValue, (String) newValue)) {
				return true;
			}
		}
		T object = get(propertyName, newValue);
		return (object == null);
	}
	
	public boolean isExist(String propertyName, Object value) {
		Assert.hasText(propertyName, "propertyName must not be empty");
		Assert.notNull(value, "value is required");
		T object = get(propertyName, value);
		return (object != null);
	}

	@SuppressWarnings("unchecked")
	public PK save(T entity) {
		Assert.notNull(entity, "entity is required");
		return (PK) getSession().save(entity);
	}

	public void persist(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().persist(entity);
	}
	
	public void update(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().update(entity);
	}

	public void merge(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().merge(entity);
	}
	
	public void delete(T entity) {
		Assert.notNull(entity, "entity is required");
		getSession().delete(entity);
	}

	public void delete(PK id) {
		Assert.notNull(id, "id is required");
		T entity = load(id);
		getSession().delete(entity);
	}

	public void delete(PK[] ids) {
		Assert.notEmpty(ids, "ids must not be empty");
		for (PK id : ids) {
			T entity = load(id);
			getSession().delete(entity);
		}
	}

	public void flush() {
		getSession().flush();
	}

	public void clear() {
		getSession().clear();
	}

	public void evict(Object object) {
		Assert.notNull(object, "object is required");
		getSession().evict(object);
	}
	
	public Pager<T> findByPager(Pager<T> pager) {
		DetachedCriteria detachedCriteria = DetachedCriteria.forClass(entityClass);
		return findByPager(pager, detachedCriteria);
	}

	@SuppressWarnings("unchecked")
	public Pager<T> findByPager(Pager<T> pager, DetachedCriteria detachedCriteria) {
		Integer start = pager.getStart();
		Integer limit = pager.getLimit();
		String orderBy = pager.getSort();
		String dir = pager.getDir();
		
		Criteria criteria = detachedCriteria.getExecutableCriteria(getSession());
		
		initCriteria(criteria, pager);
		
		Long totalCount = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
		if(totalCount == null) {
			totalCount = new Long(0);
		}
		//设置分页排序等参数
		criteria.setProjection(null);
		criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
		criteria.setFirstResult(start);
		criteria.setMaxResults(limit);
		if (StringUtils.isNotEmpty(orderBy) && dir != null) {
			if (dir.equals(Pager.DIR_ASC)) {
				criteria.addOrder(Order.asc(orderBy));
			} else {
				criteria.addOrder(Order.desc(orderBy));
			}
		}
		pager.setTotalCount(totalCount.intValue());
		pager.setList(criteria.list());
		return pager;
	}

	/**
	 * 初始化criteria的查询条件
	 * @param criteria 
	 * @param pager
	 */
	private void initCriteria(Criteria criteria,Pager<T> pager){
		//设置查询条件
		List<Param> params = pager.getParams();
		
		for (Param param : params) {
			String key = param.getName();
			if (StringUtils.isNotEmpty(key)) {
				String propertyString = "";
				if (key.contains(".")) {
					String propertyPrefix = StringUtils.substringBefore(key, ".");
					String propertySuffix = StringUtils.substringAfter(key, ".");
					criteria.createAlias(propertyPrefix, "model");
					propertyString = "model." + propertySuffix;
				} else {
					propertyString = key;
				}
				if(param.getOperators().equals("eq")){
					criteria.add(Restrictions.eq(propertyString, param.getValue()));
				} else if(param.getOperators().equals("ge")){
					criteria.add(Restrictions.ge(propertyString, param.getValue()));
				} else if(param.getOperators().equals("gt")){
					criteria.add(Restrictions.gt(propertyString, param.getValue()));
				}else if(param.getOperators().equals("le")){
					criteria.add(Restrictions.le(propertyString, param.getValue()));
				}else if(param.getOperators().equals("lt")) {
					criteria.add(Restrictions.lt(propertyString, param.getValue()));
				}else if(param.getOperators().equals("like")){
					criteria.add(Restrictions.like(propertyString, "%"+param.getValue()+"%"));
				}
			}
		}
		
		if(!StringUtils.isEmpty(pager.getKeyword())) {
			queryParamsCallBack(criteria, pager);
		}
	}
	
	/**
	 * 用来修改criteria的回调
	 * @param criteria 
	 * @param pager 参数
	 */
	protected void queryParamsCallBack(Criteria criteria,Pager<T> pager) {
		
	}
}